﻿using System.Windows;
using System.Windows.Input;
using System.Windows.Media.Media3D;

namespace ComplexAlloysVisualisation
{
    public class MouseHelper
    {
        private Camera camera;
        private FrameworkElement eventSource;
        private ModelVisual3D geometry;
        private double totalDx;
        private double totalDy;
        private bool isMouseDown;
        private Point mouseLastPosition;
        private Transform3DGroup groupTrans;
        private readonly Vector3D thetaAxis = new Vector3D(0, 1, 0);
        private readonly Vector3D phiAxis = new Vector3D(1, 0, 0);

        internal bool IsAltDown { get; set; }

        /// <summary>
        /// Defines rotation of modles using the mouse
        /// </summary>
        /// <param name="camera"></param>
        /// <param name="geometry"></param>
        /// <param name="eventSource"></param>
        public MouseHelper(Camera camera, ModelVisual3D geometry, FrameworkElement eventSource)
        {
            this.EventSource = eventSource;
            this.camera = camera;
            this.geometry = geometry;
            groupTrans = geometry.Transform as Transform3DGroup;
            groupTrans.Children.Add(new RotateTransform3D(new QuaternionRotation3D()));
            groupTrans.Children.Add(new RotateTransform3D(new QuaternionRotation3D()));
            IsAltDown = false;
        }

        public FrameworkElement EventSource
        {
            get { return eventSource; }
            set
            {
                if (eventSource != null)
                {
                    eventSource.MouseUp -= this.Model_MouseUp;
                    eventSource.MouseDown -= this.Model_MouseDown;
                    eventSource.MouseMove -= this.Model_MouseMove;
                }
                eventSource = value;
                eventSource.MouseUp += this.Model_MouseUp;
                eventSource.MouseDown += this.Model_MouseDown;
                eventSource.MouseMove += this.Model_MouseMove;
            }
        }

        private void Model_MouseUp(object sender, MouseButtonEventArgs e)
        {
            isMouseDown = false;
        }

        private void Model_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.LeftButton != MouseButtonState.Pressed) return;
            isMouseDown = true;
            Point pos = Mouse.GetPosition(eventSource);
            mouseLastPosition = new Point(
                    pos.X - eventSource.ActualWidth / 2,
                    eventSource.ActualHeight / 2 - pos.Y);
        }

        private void Model_MouseMove(object sender, MouseEventArgs e)
        {
            if (!isMouseDown) return;
            if (IsAltDown) return;
            Point pos = Mouse.GetPosition(eventSource);
            Point actualPos = new Point(
                    pos.X - eventSource.ActualWidth / 2,
                    eventSource.ActualHeight / 2 - pos.Y);

            double dx = actualPos.X - mouseLastPosition.X;
            double dy = actualPos.Y - mouseLastPosition.Y;

            totalDx += dx;
            totalDy += dy;

            double theta = totalDx / 3.0;
            double phi = totalDy / 3.0;

            var rotX = (QuaternionRotation3D)((RotateTransform3D)groupTrans.Children[0]).Rotation;
            rotX.Quaternion = new Quaternion(thetaAxis, theta);

            var rotY = (QuaternionRotation3D)((RotateTransform3D)groupTrans.Children[1]).Rotation;
            rotY.Quaternion = new Quaternion(phiAxis, phi);

            mouseLastPosition = actualPos;
        }

        internal void Reset()
        {
            totalDx = 0.0;
            totalDy = 0.0;
            groupTrans.Children.Clear();
            groupTrans.Children.Add(new RotateTransform3D(new QuaternionRotation3D()));
            groupTrans.Children.Add(new RotateTransform3D(new QuaternionRotation3D()));
        }
    }
}